﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using libZPlay;
using System.Timers;

namespace XinChen
{
    class Audio
    {
        private static int instance = 0;//计算实例化的总数量

        /// <summary>
        /// 播放信息，PlayingUpdate事件触发时更新
        /// </summary>
        public class PlayerUpdateArgs : EventArgs
        {
            /// <summary>
            /// 左声道音量（0-100）
            /// </summary>
            public int VolumeLeft { get; protected set; }
            /// <summary>
            /// 右声道音量（0-100）
            /// </summary>
            public int VolumeRight { get; protected set; }
            /// <summary>
            /// 当前播放位置（秒，带三位小数精确到毫秒）
            /// </summary>
            public float CurrentPos { get; protected set; }

            public PlayerUpdateArgs(int left, int right, int pos_ms)
            {
                VolumeLeft = left;
                VolumeRight = right;
                CurrentPos = (float)(pos_ms / 1000);
            }
        }
        public delegate void PlayerUpdateHandler(object sender, PlayerUpdateArgs e);
        /// <summary>
        /// 播放途中定时引发的事件，提醒用户界面更新
        /// </summary>
        public event PlayerUpdateHandler PlayingUpdate;
        /// <summary>
        /// 事件PlayingUpdate触发的频率，默认100（单位毫秒）
        /// </summary>
        public int PlayingUpdate_Interval
        {
            get
            {
                return (int)timer_Playing.Interval;
            }
            set
            {
                timer_Playing.Interval = value;
            }
        }
        /// <summary>
        /// 播放停止事件，提醒用户作相应处理
        /// </summary>
        public event EventHandler PlayerStop;

        /// <summary>
        /// 创建实例是否成功，目前仅受instance数量限制
        /// </summary>
        public bool Ready { get; protected set; }
        /// <summary>
        /// 当前实例的状态枚举
        /// </summary>
        public enum PlayerStatus
        {
            Stop = 0,
            Play,
            Record,
            Convert
        }
        /// <summary>
        /// 获取当前实例的状态
        /// </summary>
        public PlayerStatus Status { get; protected set; }
        /// <summary>
        /// 当前播放歌曲总长度（秒，带三位小数精确到毫秒）
        /// </summary>
        public float SongLength { get { return (float)(info.Length.ms / 1000); } }

        private ZPlay player;
        private TCallbackFunc ZPlayCallBackFunc;//回调函数，需要用全局变量来存放避免被自动垃圾回收，否则会使回调指针溢出。
        private Timer timer_Playing;
        private TStreamInfo info;

        #region 初始化，事件注册处理
        public Audio()
        {
            Audio.instance++;
            if (Audio.instance > 2) //控制不能超过2个实例，仅作预留控制
            {
                Ready = false;
                return;
            }

            //实例化，初始状态为停止
            player = new ZPlay();
            info = new TStreamInfo();
            Status = PlayerStatus.Stop;
            //PlayingUpdate事件使用的定时触发器，播放中激活
            timer_Playing = new Timer();
            timer_Playing.Interval = 100;
            timer_Playing.AutoReset = true;
            timer_Playing.Enabled = false;
            timer_Playing.Elapsed += new ElapsedEventHandler(timer_Playing_Elapsed);
            //回调函数，需要用全局变量来存放避免被自动垃圾回收，否则会使回调指针溢出。
            ZPlayCallBackFunc = new TCallbackFunc(playerCallBack);
            player.SetCallbackFunc(ZPlayCallBackFunc, TCallbackMessage.MsgPlayAsync | TCallbackMessage.MsgStopAsync | TCallbackMessage.MsgNextSongAsync, 0);

            //频谱图设置，调用DrawFFTonPictureBox来更新频谱图
            player.SetFFTGraphParam(TFFTGraphParamID.gpGraphType, (int)TFFTGraphType.gtAreaLeftOnTop);//样式
            player.SetFFTGraphParam(TFFTGraphParamID.gpHorizontalScale, (int)TFFTGraphHorizontalScale.gsLinear);//线性显示频谱区间
            player.SetFFTGraphParam(TFFTGraphParamID.gpFrequencyScaleVisible, 0);//横坐标不显示
            player.SetFFTGraphParam(TFFTGraphParamID.gpDecibelScaleVisible, 0);//纵坐标不显示
            player.SetFFTGraphParam(TFFTGraphParamID.gpFFTPoints, (int)Math.Pow(2, 8));//采样精度
        }
        ~Audio()
        {
            Audio.instance--;
            if (Ready)
            {
                timer_Playing.Dispose();
                player.Close();
                player.SetCallbackFunc(null, 0, 0);
            }
        }
        /// <summary>
        /// 通过定时器触发PlayingUpdate事件，并送出相关播放信息
        /// </summary>
        private void timer_Playing_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (PlayingUpdate != null)//如果用户没有订阅事件，就会出现这个对象为Null
            {
                int left = 0, rigth = 0;
                player.GetVUData(ref left, ref rigth);
                TStreamTime pos = new TStreamTime();
                player.GetPosition(ref pos);

                PlayingUpdate(this, new PlayerUpdateArgs(left, rigth, (int)pos.ms));
            }
        }
        /// <summary>
        /// 将频谱画到指定的PictureBox上
        /// </summary>
        /// <param name="pBox">PictureBox控件，最小尺寸为100x60</param>
        public bool DrawFFTonPictureBox(System.Windows.Forms.PictureBox pBox)
        {
            if (pBox.Width < (int)TFFTGraphSize.FFTGraphMinWidth || pBox.Height < (int)TFFTGraphSize.FFTGraphMinHeight)
            {
                return false;
            }
            System.Drawing.Graphics g = pBox.CreateGraphics();
            IntPtr hdc = g.GetHdc();
            bool Drawed = player.DrawFFTGraphOnHDC(hdc, 0, 0, pBox.Width, pBox.Height);
            g.ReleaseHdc(hdc);
            g.Dispose();
            return Drawed;
        }
        /// <summary>
        /// libZPlay的回调函数，主要用于处理各种订阅的消息循环
        /// </summary>
        private int playerCallBack(uint objptr, int user_data, TCallbackMessage msg, uint param1, uint param2)
        {
            switch (msg)
            {
                case TCallbackMessage.MsgStopAsync:
                case TCallbackMessage.MsgStop:
                    timer_Playing.Stop();
                    Status = PlayerStatus.Stop;
                    //引发停止事件，通知主程序进行处理。
                    if (PlayerStop != null)//如果用户没有订阅事件，就会出现这个对象为Null
                    {
                        PlayerStop(this, new EventArgs());
                    }
                    break;

                case TCallbackMessage.MsgPlayAsync:
                case TCallbackMessage.MsgPlay:
                    //有可能是在播放，录音或转换中，由具体函数负责更新状态。
                    timer_Playing.Start();
                    player.GetStreamInfo(ref info);
                    break;

                case TCallbackMessage.MsgEnterLoopAsync:
                case TCallbackMessage.MsgEnterLoop:
                    break;

                case TCallbackMessage.MsgExitLoopAsync:
                case TCallbackMessage.MsgExitLoop:
                    break;

                case TCallbackMessage.MsgEnterVolumeSlideAsync:
                case TCallbackMessage.MsgEnterVolumeSlide:
                    break;

                case TCallbackMessage.MsgExitVolumeSlideAsync:
                case TCallbackMessage.MsgExitVolumeSlide:
                    break;

                case TCallbackMessage.MsgStreamBufferDoneAsync:
                case TCallbackMessage.MsgStreamBufferDone:
                    break;

                case TCallbackMessage.MsgStreamNeedMoreDataAsync:
                case TCallbackMessage.MsgStreamNeedMoreData:
                    break;

                case TCallbackMessage.MsgNextSongAsync:
                case TCallbackMessage.MsgNextSong:
                    //列表播放时，换歌引发，更新当前歌曲信息
                    player.GetStreamInfo(ref info);
                    break;

                case TCallbackMessage.MsgWaveBuffer:
                    break;

                default:
                    break;
            }
            return 0;
        }
        #endregion

        #region 打开文件或文件列表
        /// <summary>
        /// 打开一个音乐文件，自动检测文件格式
        /// </summary>
        /// <param name="filePath">完整的路径名称</param>
        /// <returns>是否成功</returns>
        public bool OpenFile(string filePath)
        {
            if (player.OpenFile(filePath, TStreamFormat.sfAutodetect))
            {
                player.GetStreamInfo(ref info);
                return true;
            }
            info = new TStreamInfo();
            return false;
        }
        /// <summary>
        /// 将一批音乐文件加入播放列表
        /// </summary>
        /// <param name="filePaths">完整的路径名称清单</param>
        /// <returns>是否所有都歌曲被成功加入</returns>
        public bool OpenFile(string[] filePaths)
        {
            //清空当前播放列表
            player.Close();

            foreach (string filePath in filePaths)
            {
                if (!player.AddFile(filePath, TStreamFormat.sfAutodetect))
                {
                    player.Close();
                    info = new TStreamInfo();
                    return false;
                }
            }
            player.GetStreamInfo(ref info);
            return true;
        }
        #endregion

        /// <summary>
        /// 停止当前播放，录音，转换
        /// </summary>
        /// <returns>是否成功</returns>
        public bool Stop()
        {
            return player.StopPlayback();
        }

        #region 播放控制
        /// <summary>
        /// 快速播放音乐文件
        /// </summary>
        /// <param name="FilePath">文件完整路径</param>
        /// <returns>是否成功</returns>
        public static bool Play(string FilePath)
        {
            ZPlay player = new ZPlay();
            if (player.OpenFile(FilePath, TStreamFormat.sfAutodetect))
            {
                return player.StartPlayback();
            }
            return false;
        }

        /// <summary>
        /// 播放已经打开的文件
        /// </summary>
        /// <returns>是否成功</returns>
        public bool Play()
        {
            if (player.StartPlayback())
            {
                Status = PlayerStatus.Play;
                return true;
            }
            player.StopPlayback();
            return false;
        }
        /// <summary>
        /// 播放已打开的文件，从指定的位置开始，播放一定长度
        /// </summary>
        /// <param name="StartPos">开始位置的秒数（秒，带三位小数精确到毫秒）</param>
        /// <param name="Length">停止位置的秒数，为零则播放到结束（秒，带三位小数精确到毫秒）</param>
        /// <param name="Loop">循环次数，1不循环（播一次），不可以小于1</param>
        /// <returns>是否成功</returns>
        public bool Play(float StartPos, float EndPos, int Loop)
        {
            if (Loop > 0)
            {   //循环次数必须大于0
                TStreamTime start = new TStreamTime();
                start.ms = (uint)(StartPos * 1000);
                TStreamTime end = new TStreamTime();
                player.GetStreamInfo(ref info);
                if (EndPos == 0 || EndPos * 1000 > info.Length.ms)
                {
                    end.ms = info.Length.ms;
                }
                else
                {
                    end.ms = (uint)(EndPos * 1000);
                }

                if (player.PlayLoop(TTimeFormat.tfMillisecond, ref start, TTimeFormat.tfMillisecond, ref end, (uint)Loop, false))
                {
                    Status = PlayerStatus.Play;
                    return true;
                }
            }
            player.StopPlayback();
            return false;
        }
        /// <summary>
        /// 定位到歌曲指定位置
        /// </summary>
        /// <param name="Pos">从0开始到定位点的秒数（秒，带三位小数精确到毫秒）</param>
        /// <returns>是否成功</returns>
        public bool Seek(float Pos)
        {
            TStreamTime _pos = new TStreamTime();
            _pos.ms = (uint)(Pos * 1000);
            if (_pos.ms > info.Length.ms) { return false; }
            return player.Seek(TTimeFormat.tfMillisecond, ref _pos, TSeekMethod.smFromBeginning);
        }
        #endregion

        /// <summary>
        /// 录音或转换时用的输出格式
        /// </summary>
        public enum Format
        {
            MP3 = 0,
            WAV,
            FLAC,
            Ogg,
            AAC,
            /// <summary>
            /// 为Dragon语音识别引擎预留的类型，Wave:format_22khz_16bit_mono
            /// </summary>
            DNS
        }
        private TStreamFormat getFormat(Format format)
        {
            TStreamFormat _format;
            switch (format)
            {
                case Format.MP3:
                    _format = TStreamFormat.sfMp3;
                    break;
                case Format.WAV:
                    _format = TStreamFormat.sfWav;
                    break;
                case Format.DNS:
                    _format = TStreamFormat.sfWav;
                    break;
                default:
                    _format = TStreamFormat.sfOgg;
                    break;
            }
            return _format;
        }

        /// <summary>
        /// 从麦克风录音到指定文件，函数不检查文件扩展名，直接按指定格式录音。
        /// </summary>
        /// <param name="filePath">完整录音文件名</param>
        /// <param name="recFormat">录音格式</param>
        /// <returns>是否开始录音</returns>
        public bool Rec2File(string filePath, Format recFormat)
        {
            if (player.OpenFile("wavein://src=microphone;volume=80;", TStreamFormat.sfAutodetect) && player.SetWaveOutFile(filePath, getFormat(recFormat), true))
            {
                Status = PlayerStatus.Record;
                info = new TStreamInfo();
                player.StartPlayback();
                return true;
            }
            return false;
        }

        /// <summary>
        /// 格式转换
        /// </summary>
        /// <param name="fromFile">源文件路径</param>
        /// <param name="toFile">目标文件路径</param>
        /// <param name="toFormat">目标格式</param>
        /// <returns>是否开始转换</returns>
        public bool Convert(string fromFile, string toFile, Format toFormat)
        {
            if (OpenFile(fromFile) && player.SetWaveOutFile(toFile, getFormat(toFormat), false))
            {
                Status = PlayerStatus.Convert;
                player.StartPlayback();
                return true;
            }
            return false;
        }
        /// <summary>
        /// 合并多个文件，支持同时格式转换
        /// </summary>
        /// <param name="fromFile">源文件列表</param>
        /// <param name="toFile">目标文件路径</param>
        /// <param name="toFormat">目标格式</param>
        /// <returns>是否开始转换</returns>
        public bool Convert(string[] fromFiles, string toFile, Format toFormat)
        {
            if (OpenFile(fromFiles) && player.SetWaveOutFile(toFile, getFormat(toFormat), false))
            {
                Status = PlayerStatus.Convert;
                player.StartPlayback();
                return true;
            }
            return false;
        }

    }
}
